#!/usr/bin/python
# -*- coding: utf-8 -*-
# @version        : 1.0
# @Creaet Time    : 2021/9/29 22:39 
# @File           : run.py
# @IDE            : PyCharm
# @desc           : fastapi 学习项目

"""
中文官网文档：https://fastapi.tiangolo.com/zh/
FastAPI官方文档教程完整翻译：https://blog.csdn.net/xuqingskywolf/category_10022295.html
FastAPI B站讲解：https://www.bilibili.com/video/BV1iN411X72b?p=1
B站视频源码：https://github.com/liaogx/fastapi-tutorial

官方推荐 FastAPI Web项目生成器：https://github.com/tiangolo/full-stack-fastapi-postgresql
FastAPI + MySQL Web项目生成器：https://github.com/CoderCharm/fastapi-mysql-generator

FastPI 相关第三方扩展：https://github.com/mjhea0/awesome-fastapi
"""


"""
CORS 跨域解决：https://fastapi.tiangolo.com/zh/tutorial/cors/
"""


from fastapi.encoders import jsonable_encoder
from starlette.staticfiles import StaticFiles   # 依赖安装：pip install aiofiles
from fastapi import FastAPI, Request
import uvicorn
from study import *  # 引入应用
from sql_app.main import app as sql_app
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.exceptions import RequestValidationError
from starlette import status
import time

app = FastAPI(
    title="FastAPI 学习项目",
    description="主要讲解了 FastAPI 的基本使用，并完整开发一个项目。",
    version="1.0.0",
)


"""
其他配置：
docs_url：配置交互文档的路由地址，如果禁用则为None，默认为 /docs
redoc_url： 配置Redoc文档的路由地址，如果禁用则为None，默认为 /redoc
openapi_url：配置接口文件json数据文件路由地址，如果禁用则为None，默认为/openapi.json
"""

# 引入应用中的路由
app.include_router(case01, prefix="/study/case01", tags=["案例1，请求操作"])
app.include_router(case02, prefix="/study/case02", tags=["案例2，特殊请求操作"])
app.include_router(case03, prefix="/study/case03", tags=["案例3，响应操作"])
app.include_router(case04, prefix="/study/case04", tags=["案例4，依赖注入"])
app.include_router(case05, prefix="/study/case05", tags=["案例5，安全认证"])
app.include_router(case06, prefix="/study/case06", tags=["案例6，数据库操作"])
app.include_router(sql_app, prefix="/study/sql", tags=["数据库操作实例"])
app.include_router(case07, prefix="/study/case07", tags=["案例7，中间件"])
app.include_router(case08, prefix="/study/case08", tags=["案例8，后台任务"])


# 挂载静态目录，并添加路由访问，此路由不会在接口文档中显示
app.mount("/static", app=StaticFiles(directory="./static"))


# 重写HTTPException异常处理器
@app.exception_handler(StarletteHTTPException)
async def unicorn_exception_handler(request: Request, exc: StarletteHTTPException):
    """
    request 参数不能省略不写
    """
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "message": exc.detail,
        }
    )


# 重写请求验证异常处理器
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """
    request 参数不能省略不写
    """
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content=jsonable_encoder({"message": exc.errors(), "body": exc.body}),
    )


# 中间件
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    """请求前操作"""
    start_time = time.time()

    response = await call_next(request)

    """响应前操作"""
    process_time = time.time() - start_time
    """
    在响应对象中添加请求头
    
    提示：请记住可以 用'X-' 前缀添加专有自定义请求头.
    """
    response.headers["X-Process-Time"] = str(process_time)

    return response


if __name__ == '__main__':
    # 启动项目
    # reload：自动重载项目
    # debug：调试
    # workers：启动几个进程
    uvicorn.run('run:app', host="127.0.0.1", port=8000, reload=True, debug=True, workers=1)